﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using MazeBuilder.Entities;
using MazeBuilder.Entities.Tiles;
using RoomModuleCatalog;

namespace MazeBuilder
{
  public class MazeBuilder
  {
    private Maze maze;
    private Digger digger;

    public event oneBuilded oneBuilded;
    public event Builded builded;

    public void OnBuilded(BuildedArgs args)
    {
      var handler = builded;
      if (handler != null) handler(this, args);
    }

    public void OnOneBuilded(oneBuildedArgs args)
    {
      var handler = oneBuilded;
      if (handler != null) handler(this, args);
    }

    public Maze BuildMaze(IMazeConfiguration configuration) {
      var cfg = configuration;
      maze = new Maze(cfg);

      digger = DiggerFactory.GetDigger(cfg);
      digger.oneBuilded += digger_oneBuilded;
      digger.builded += digger_builded;

      digger.Build(maze);

      maze.tiles = Trim(maze.tiles);
      return maze;
    }

    private void digger_builded(object sender, buildedArgs args)
    {
      OnBuilded(new BuildedArgs());  
    }

    void digger_oneBuilded(object sender, RoomBuildedArgs args) {
      OnOneBuilded(new oneBuildedArgs());
    }

    private static Tile[][] Trim(IList<Tile[]> tiles) {
      var tt = tiles.SelectMany(list => list).Where(x => x.ID > 0);

      var minRow = tt.Min(x => x.Row) - 1;
      var maxRow = tt.Max(x => x.Row) + 2;
      var minCol = tt.Min(x => x.Col) - 1;
      var maxCol = tt.Max(x => x.Col) + 2;

      var newHeight = maxRow - minRow;
      var newWidth = maxCol - minCol;
      
      var newTiles = new Tile[newHeight][];
      newTiles.Initialize();
      
      for (var i = 0; i < newTiles.GetLength(0); i++)
        newTiles[i] = new Tile[newWidth];

      var row = 0;
      for (var i = minRow; i < maxRow; i++) {
        var col = 0;
        for (var j = minCol; j < maxCol; j++) {
          newTiles[row][col] = tiles[i][j];
          col++;
        }
        row++;
      }
      return newTiles;
    }
  }

  public delegate void Builded(object sender, BuildedArgs args);

  public class BuildedArgs
  {
  }

  public delegate void oneBuilded(object sender, oneBuildedArgs args);
  public class oneBuildedArgs
  {
  }

  public static class StaticRandomNumberGenerator
  {
    private static readonly RandomNumberGenerator RandomGenerator = RandomNumberGenerator.Create();

    /// <exception cref="ArgumentOutOfRangeException"><c>min</c> is out of range.</exception>
    public static int Next(int min, int max) {
      if (min > max) throw new ArgumentOutOfRangeException("min");
      if (min == max) return min;
      var bytes = new byte[4];

      RandomGenerator.GetBytes(bytes);

      var next = BitConverter.ToUInt32(bytes, 0);
      var range = max - min;

      return (int)((next % range) + min);
    }
  }
}
